/*
 * DefaultRegister.java   2008/07/28
 *
 * Copyright 2008 Web & Zen Multimedia, All rights reserved.
 * Use is subject to license terms.
 */
package it.wzm.lightweightlog;

/*
import java.io.ByteArrayOutputStream;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
 */
import java.util.Iterator;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.Properties;
import java.lang.management.*;
import javax.management.*;

/**
 * Default implementation of Register, it simply extends Register and store passed Dumper
 * into an ArrayList, called slot.
 */
final class ThreadedRegister<T extends Dumper> extends Register<T> implements Serializable {

	/**
     * As required by serialization
     */
	private static final long serialVersionUID = -1107606779436763082L;
    
    /**
     * Where all the Dumper are stored
     */
    private LinkedList<T> slot;
	/** 
	 * where all our Threads will be stored...
	 */
	private LinkedList<Thread> threads;

    /**
     * Create register from a given Array of Dumper, null array cause ObjectInitializationException.
     * If in the passed array some Dumper index is null.
     * @param t Array of dumper
     */
    public ThreadedRegister(Properties p, T... t) {
        // doing our Duty...
        super(p,t);
    }

    @Override
    final void preinit() {
    	// this where our Dumper will be saved...
    	this.slot = new LinkedList<T>();
    }
    
    @Override final void postinit() {
    	// where all of our thread will be stored...
    	this.threads = new LinkedList<Thread>();  	
    }
    
    /**
     * Save a passed Dumper into the slot, if the slot already contains this Dumper,
     * then nothing is stored and false is returned.
     * @param t Anything extending Dumper
     * @return true if Dumper is properly added false if not
     */
    boolean save(T t) {
        return slot.add(t);
    }

    /**
     * Remove a passed Dumper from the slot, if the slot already does not contains this Dumper,
     * or the Dumper status is not FAILURE then nothing is removed and false is returned.
     * @param t Anything extending Dumper
     * @return true if Dumper is properly added false if not
     */
    boolean remove(T t) {
        // easy check...
        if (!slot.contains(t)) {
            return false;
        // only Dumper with status FAILURE can be removed
        }
        if (t.getStatus() == Dumper.Status.FAILURE) {
            return slot.remove(t);
        } else {
            return false;
        }
    }

    /**
     * The size of the slot
     * @return int
     */
    int size() {
        return slot.size();
    }

    /**
     * Dispatch entries to each Dumper in slot
     * @param ll A LinkedList of Entry
     */
    synchronized <E extends Entry> void dispatchToDumper(LinkedList<E> ll) {
   		
        // get dumper from our slot
        Iterator<T> i = slot.iterator();

        // browse each Dumper
        dumping:
        while (i.hasNext()) {
            // get dumper from iterator
            T t = i.next();

            // get status...
            if (t.getStatus() == Dumper.Status.FAILURE && t.hasKillOnFailure()) {
            	this.remove(t);
                continue dumping;
            }

            final class Wrapper implements Runnable {          	

            	private Dumper pd;
            	private LinkedList<E> pll;
            	
            	// the only constructor
            	public Wrapper(Dumper d, LinkedList<E> ll) {
            	//public Runnable(Dumper d, LinkedList<E> ll) {
            		this.pd=d;
            		this.pll=ll;
            	}
           	
            	// implement run...
            	public void run() {
            		//Log.serviceLog(pd.getName() + ":" + pll.size());
		            try {
		                // opening
		                this.pd.prepare();
		                // browse each Entry in list
		                // we know by contract that
		                // our passed list contains
		                // a sorted group of Entries
		                Iterator<E> lbi = this.pll.iterator();
		                
		                // finally dumping to Dumper
		                // any error verified while dumping
		                // our Entry will cause the Dumper to be invalid...
		                // depending on the variable
		                while (lbi.hasNext()) {
		                	//Log.serviceLog("dump...");
		                    this.pd.dump(lbi.next());
		                }
		                
		                //notifyAll();
		
		            } catch (DumperException ex) {
		                Log.serviceLog(ex);
		            } finally {
		                try {
		                    // finally closing...
		                    this.pd.release();
		                } catch (DumperException ex) {
		                    Log.serviceLog("Cannot close Dumper: " + ex.getMessage());
		                }
		            }
            	}
	        };
	        
	        Thread td = new Thread(new Wrapper(t,ll));
	        td.setName(t.getName());
	        td.start();
	        threads.add(td);
        }
        
        // iterate and perform join() action
        Iterator<Thread> it = threads.iterator();
        while (it.hasNext()) {
        	try {
				it.next().join();
			} catch (InterruptedException e) {
				Log.serviceLog(e);
			}
        }
        // cleaning resources...
        threads.clear();        
    }    

    /**
     * Dispatch a single Entry to all Dumper in the slot
     * @param e Anything extending Entry
     */
    <E extends Entry> void dispatchToDumper(E e) {
        // get dumper from our slot
        Iterator<T> i = slot.iterator();

        // browse each Dumper
        while (i.hasNext()) {
            try {
                // get dumper from iterator
                Dumper d = i.next();
                // the following is normal routine
                d.prepare();
                d.dump(e);
                d.release();
            } catch (DumperException ex) {
                Log.serviceLog(ex);
            }
        }
    }
}
